package BLC

import (
	"bytes"
	"crypto/sha256"
	"fmt"
	"math/big"
)

type ProotOfWork struct {
	Block  *Block   // 当前要检验的区块
	target *big.Int // 大数据存储
}

// 256位hash里面前面至少要又16个零
const targetBit = 16


func (pow *ProotOfWork) prepareData(nonce int) []byte {
	data := bytes.Join(
		[][]byte{
			pow.Block.PrevBlockHash,
			pow.Block.Data,
			IntToHex(pow.Block.Timestamp),
			IntToHex(int64(targetBit)),
			IntToHex(int64(nonce)),
			IntToHex(int64(pow.Block.Height)),
		},
		[]byte{})
	return data
}

func (proofOfWork *ProotOfWork) IsValid() bool {

	// 1.proofOfWork.Block.Hash
	//2.proofOWork.Target

	var hashInt  big.Int
	hashInt.SetBytes(proofOfWork.Block.Hash)

	if proofOfWork.target.Cmp( &hashInt) == 1 {
		return true
	}

	return false
}

func (prootOfWork *ProotOfWork) Run() ([]byte, int64) {

	//1. 将Block的属性拼接成字符串
	//2. 生成hash
	//3. 判断hash有效性，

	nonce := 0

	var hashInt big.Int
	var hash [32]byte

	for {
		// 准备数据 block属性拼接成字符串
		dataBytes := prootOfWork.prepareData(nonce)

		// 生成hash
		hash = sha256.Sum256(dataBytes)
		fmt.Printf("\r%x", hash)

		// 将hash 存储到hashInt
		hashInt.SetBytes(hash[:])
		//fmt.Println()
		//fmt.Println("hashInt = " , hashInt.Int64())

		// 判断block里面的target 是否大于 hashInt
		if prootOfWork.target.Cmp(&hashInt) == 1 {
			break
		}

		nonce = nonce + 1

	}

	return hash[:], int64(nonce)
}

//创建新的工作证明对象
func NewProofOfWork(block *Block) *ProotOfWork {

	//1、创建一个初始的1的target
	target := big.NewInt(1)

	// 2. 左移256 - targetBit
	target = target.Lsh(target, 256-targetBit)

	return &ProotOfWork{block, target}

}
